首先我們回顧先前文章 D18. 做個 list 裡提到的 index 索引值,裡面提過它是從 0 開始計算的,但沒有詳細說明到每個 index 跟其所串聯的資料沒有相關性。
簡單的說,例如範本裡的 Avicii, Lenka, Shakira 依序顯示出索引值的話會如下所示:
Avicii
Lenka
Shakira
一但透過操作 array 的順序變化或者是增刪,只有資料的順序、內容會變動,索引值會維持原本的順序或值去顯示,並不會產生相對應的行為。
例如移動 Avicii 到最後面,則顯示如下:
Lenka
Shakira
Avicii
若刪除 Shakira 這個值則顯示如下:
Lenka
Avicii
從上面的範例,應該可以明確的理解到,index 只是單純地列出有幾個值( 而且都從 0 開始 ),並不會跟資料建立互相的綁定或產生相依性,就單純做它自己,靜靜地顯示而已。
基於 index 不綁定的原因,當程式需要**建立具有相依性以及互相綁定的值時,會使用 key **來建立,它用於使 v-for 不要 loop 出超出預期的狀態。
也就是我們可以預期到使用前列範例去列出 0, 1, 2 的時候,它可以真正的去移動以及被刪增,例如移動 Avicii 到最後面,則顯示如下:
Lenka
Shakira
Avicii
又或刪除 Shakira 這個值可以顯示如下:
Lenka
Avicii
可以觀察到 key 與 data 具有相依性。
更詳細的說明可以參考官方網站的說明: Maintaining State with key。
什麼是超出預期的狀態,前面已經明確地列出 index 與 key 的不同,那麼換個方式來實作,按照課程範例,可以先修改 template 的內容並新增一個 “ move ” 按鈕( 原本的 json 資料結構請不要改動 )。
<ul class="text-left">
<li v-for="musicDetail in musicDetails" class="mb-3">
{{ musicDetail.name }} : {{ musicDetail.quote }} <small>{{ musicDetail.from }}</small><br />
<input type="text" class="w-full"/>
</li>
</ul>
<button type="button" v-on:click="move" class="border-green-600 m-3">Move to bottom</button>
注意,我有使用 TailWindCSS,所以才會加上一些 class 設定,若是不需要或者要自行加 css 是完全可以的唷!
接著給予相對應的 function 到 methods 裡。
move(){
const first = this.musicDetails.shift()
this.musicDetails.push(first)
}
這裡先命名了 first 這個變數來呼叫 array 裡第一個物件的值 ( 在此是指 Avici,它位於 array 裡第一個位置唷 ),再以 shift() 將 Avici 移除掉並把 array 裡第二個物件的值往前移動,也就是說把 Lenca 移動到最前面,這時候 array 就渲染出 Lenca, Shakira,詳請參考 Array.prototype.shift()。
接著以 push(first) 來將 Avici 新增到 array 最後面的順序,所以 array 渲染出的就變成 Lenca, Shakira, Avici,詳請參考 Array.prototype.push()。
有點難以理解的話,就把 code 貼上去體驗,圖示如下,預設會是
若是按了按鈕,就會呈現出下圖的變化,Avici 被移動到最下面
現在重新整理頁面,讓 Avici 排在最上面,然後在 input 裡隨便打串字,顯示的圖會如下:
但是!就是這個但是!!!
這個時候如果按了按鈕會讓隨便打的字跟著 Avici 一起跳到下面去嗎?
答案是並不會!!!!
這個看起來奇怪的狀況是因為被操作的是 data 裡的 array ,但寫在 template 裡的東西就如同 index 一樣,跟 array 裡的資料並沒有相依性,所以 input 裡的字並沒有錯,它還是原本那個它並沒有改變,這個狀態有個名詞叫做 “patching”,其目標是為了節省效能,只做資料的操作而不去動到 template ( 也就是 DOM )。
畫了一張圖,如果無法理解,希望圖可以解釋( 如果還是很困惑,請留言給我,我會再想想辦法解釋 )。
將 template 加上 key 後如下:
<ul class="text-left">
<li v-for="musicDetail in musicDetails" :key="musicDetail.name" class="mb-3">
{{ musicDetail.name }} : {{ musicDetail.quote }} <small>{{ musicDetail.from }}</small><br />
<input type="text" class="w-full"/>
</li>
</ul>
<button type="button" v-on:click="move" class="border-green-600 m-3">Move to bottom</button>
會使用 name 是因為如同之前文章所提,我貼的範例資料只有 name 是單純的詞,沒有空白唷。
這時候再重新整理頁面並隨便打入字進 input 裡,操作按鈕,就可以順利的看到整個 template 裡的資料與 key 有所綁定了( 會跟著移動 )。
點選按鈕後:
除了巢狀 v-for,其實使用 key 值去綁定資料做操作也是會消耗效能。
目前的我雖然理解了課程的說明,但一時還是不太能完整的描述或找到淺顯易懂的相關文章,因此會建議如果有任何疑問,請盡量讀讀官方文件,或者只在小型的資料切換時去做這個操作,不然在大型的資料操作時會體感到很耗效能( 課程是這樣說的,還沒操作過大型專案的我使用 “我相信你”之術 )。
雖然都是在製作 list 這個 v-for 的題目,課程也只有短短的 20 分鐘,但其實花費了三天的許多時間在這上面,只能說學習是沒有捷徑的,希望我寫的文可以為瀏覽的各位帶來真的淺顯易懂的 Aha moment!
下一篇文章將是進行一個小小迷你專案練習,使用的是從文章開始到目前所學的內容,看起來並不是很困難,應該是可以很好駕馭的,但為了健康的生活作息,還是不那麼緊湊,就順其自然地學習吧。
然後看著剩下的 22 個章節,我想真的飆不完,不過至少可以先完成一個練習專案也不錯,一起加油吧!